package moa.structure;

import java.util.*;
import java.io.*;

/**<p> This class modelises a couple of a Sequence and of
 * a collection of Transactions
 * </p>
 */
public class SequenceApparition 
{
	
	/**<p>
	 * A Vector of Transaction associated to the sequence
	 * </p>
	 */
	public Vector<Transaction> transactions;
	
	Vector<Integer> customers;
	
	int transactionsPointer, transPointer;
	
	
	
//****************************************************************
//Constructor of the class SequenceApparition
//****************************************************************
	
	/**<p>
	 * Create a new SequenceApparition with the Sequence given in parameter
	 * and an empty Vector of Transaction
	 * </p>
	 */
	public SequenceApparition()
	{
		transactions = new Vector<Transaction>();
		customers = new Vector<Integer> ();
		transactionsPointer = 0;
		transPointer = 0;
	}
	
	/**<p>
	 * Create a new SequenceApparition with the Sequence and the Vector of Transaction given in parameter
	 * </p>
	 */
	public SequenceApparition( Vector<Transaction> transactions)
	{
		this.transactions = new Vector<Transaction>();
		customers = new Vector<Integer> ();
		transactionsPointer = 0;
		transPointer = 0;
		if (!transactions.isEmpty())
		{
			for (int i=0; i<transactions.size(); i++)
			{
				addTransaction(transactions.get(i));
			}
		}
		
	}
	
//**************************************************************************************
//Methods of the class SequenceApparition
//**************************************************************************************


	/**<p>
	 * @return the Vector which contains all Transaction for the current Sequence
	 * </p>
	 */
	public Vector getTransactions() 
	{
		return transactions;
	}
	
	/**<p>
	 * @return a customer
	 * </p>
	 */
	public int getCustomer(int index)
	{
		if(index < customers.size())
		{
			return (customers.get(index).intValue());
		}
		return -1;
	}
	
	/**<p> 
	 * @return the number of customers (each one is only counted once) who support the current sequence
	 * </p>
	 */
	public int getNbCustomers()
	{
		return customers.size();
	}
	
	/**<p>
	 * @param a Vector which contains Transaction's objects
	 * </p>
	 */
	public void setTransactions(Vector<Transaction> transactions) 
	{
		this.transactions = transactions;
	}
	
	/**<p>
	 * @param index : index of the desired Transaction
	 * @return the indexth Transaction of the Vector of Transaction
	 * </p>
	 */
	public Transaction getTransaction (int index)
	{
		if (index < getTransactions().size())
		{
			return (transactions.get(index));
		}
		return null;
	}
	
	/**<p>
	 * WARNING : possible optimization to add in this method in order not to start each time 
	 * to the begining of the Vector in order to extract the subVector
	 * @param customer : the customer desired to construct the sub vector
	 * @return a subvector wich only contains the transactions of the desired customer
	 * </p>
	 */
	public Vector<Transaction> getSubTransaction (int customer)
	{
		Vector<Transaction> tmp = new Vector<Transaction> ();
		for (int i = transactionsPointer; i < transactions.size(); i++)
		{
			if(transactions.get(i).getCustomer() > customer)
			{
				transactionsPointer = i;
				break;   
			}
			if (transactions.get(i).getCustomer() == customer)
			{
				tmp.add(transactions.get(i));
			}
		}
		if(!tmp.isEmpty())
			return tmp;
		return null;
	}
	
	public Vector<Transaction> getSubTransaction (int customer, int dateMin)
	{
		Vector<Transaction> tmp = new Vector<Transaction> ();
		for (int i = transPointer; i < transactions.size(); i++)
		{
			if(transactions.get(i).getCustomer() > customer)
			{
				transPointer = i;
				break;
			}
			if ((transactions.get(i).getCustomer() == customer) && 
			    (transactions.get(i).getDate () > dateMin))
			{
				tmp.add(transactions.get(i));
			}
			
		}
		if(!tmp.isEmpty())
			return tmp;
		return null;
	}
	
	/**<p>
	 * WARNING : possible optimization to add in this method in order not to start each time 
	 * to the begining of the Vector in order to extract the subVector
	 * @param customer : the customer desired to construct the sub vector
	 * @param minG : the minimum gap
	 * @param maxG : the maximum gap
	 * @return a subvector wich only countains the transactions of the desired customer
	 * </p>
	 */
	public Vector<Transaction> getSubTransaction (int customer, int dateMin, int minG, int maxG)
	{
		//System.out.println("MAXG "+maxG);
		Vector<Transaction> tmp = new Vector<Transaction> ();
		for (int i = transPointer; i < transactions.size(); i++)
		{
			if(transactions.get(i).getCustomer() > customer)
			{
				transPointer = i;
				break;
			}
			if ((transactions.get(i).getCustomer() == customer) && 
			    (transactions.get(i).getDate () > dateMin) &&
			    (transactions.get(i).getDate () - dateMin) <= maxG &&
			    (transactions.get(i).getDate () - dateMin) > minG)
			{
				tmp.add(transactions.get(i));
			}
			
		}
		if(!tmp.isEmpty())
			return tmp;
		return null;
	}
//***************************************************************************************
//Methods of the class SequenceApparition
//***************************************************************************************


	/**<p> This method add a Transaction to the Vector of Transaction
	 * </p>
	 */
	public void addTransaction(Transaction trans)
	{
		transactions.add(trans);
		if (!customers.contains(new Integer(trans.getCustomer())))
		{
			customers.add(new Integer(trans.getCustomer()));
		}
	}
	
	/**<p>This method create a new entry in the verticalDB (creation of a new SequenceAppartion), intersecting two
	 * SequenceApparition. This intersection creates a new SequenceApparition's object where the sequence is the concatenation
	 * of the sequences of s1 & s2 and where the Vector of transaction is the intersection of the transactions of s1 and s2
	 * WARNING : - this operation MUST be optimized !!
	 * 			 - for the moment we consider that s2 is a sequence reduced to an item
	 * @param s1, s2 : the sequences we are going to intersect
	 * @return the intersection of the given parameters
	 * </p>
	 */
	public SequenceApparition intersect(SequenceApparition s)
	{
		Vector<Transaction> transactions = (Vector<Transaction>) getTransactions().clone();
		transactions.retainAll(s.getTransactions());
		if (!transactions.isEmpty())
			return(new SequenceApparition(transactions));
		return null;
	}
	
	/**<p>This method reinitializes the transactionPointer at the end of a unionSequence operation
	 * </p>
	 */
	public void resetTransactionPointer()
	{
		transactionsPointer = 0;
		transPointer = 0;
	}
	
	/**<p>This method create a new entry in the verticalDB (creation of a new SequenceAppartion) where the sequence is the concatenation
	 * of the sequences of s1 & s2 (s2, reduced to an item is inserted as a new itemset at the end of the sequence s1 => other transaction) 
	 * and where the Vector of transaction is made of transactions where the dates are the ealiest dates for "s1 and then s2" for each customer
	 * who support the created sequence.
	 * WARNING : - this operation MUST be optimized !!
	 * 			 - for the moment we consider that s2 is a sequence reduced to an item
	 * @param s1, s2 : the sequences we are going to join
	 * @return the "union" of the given parameters
	 * </p>
	 */
	public SequenceApparition unionSequences(SequenceApparition s, int minGap, int maxGap)
	{
		if (minGap == 0 && maxGap == 0)
			return unionSequences(s);
		
		//System.out.println(this+ " avec "+s);
		Vector<Transaction> transactions = new Vector<Transaction>();
		int t1, t2;
		Vector<Transaction> sub1, sub2;
		for(int i=0; i< getNbCustomers(); i++)
		{
			//for each customer who supports the sequence contained in s1
			//sub1 is a subVector of transactions supported by s1.getCustomer(i)
			sub1 = getSubTransaction(getCustomer(i));
			//t1 is the pointer on the first transaction date for the current customer
			for (int j = 0; j < sub1.size(); j++)
			{
				t1 = sub1.get(j).getDate();
				//sub2 is the subVector of transaction which supports the sequence contained 
				//in s2 and where the first date is > t1		
				sub2 = s.getSubTransaction(getCustomer(i), t1, minGap, maxGap);			
				
				if (sub2 != null)
				{
					//We keep all the transaction and not only the first in the time
					for (int k=0; k<sub2.size(); k++)
					{
						t2 = sub2.get(k).getDate();
						Transaction trans = new Transaction(getCustomer(i), t2);
						//if (!transactions.contains(trans))
							transactions.add(trans);
					}				
				}
				// Optimisation : don't do all the test if there is no time constraints
				if (minGap == 0 && maxGap == 0)
					break;
			}
		}
		resetTransactionPointer();
		s.resetTransactionPointer();
		if (!transactions.isEmpty())
		{
			//We clone s1 (faster than creating a new sequence with the same list of ItemSet
			return (new SequenceApparition(transactions));
		}
		return null;
	}
	
	public SequenceApparition unionSequences(SequenceApparition s)
	{
		Vector<Transaction> transactions = new Vector<Transaction>();
		int t1, t2;
		Vector<Transaction> sub1, sub2;
		for(int i=0; i< getNbCustomers(); i++)
		{//for each customer who supports the sequence contained in s1
			//sub1 is a subVector of transactions supported by s1.getCustomer(i)
			sub1 = getSubTransaction(getCustomer(i));
			
			//t1 is the pointer on the first transaction date for the current customer
			t1 = sub1.get(0).getDate();
			//sub2 is the subVector of transaction which supports the sequence contained 
			//in s2 and where the first date is > t1		
			sub2 = s.getSubTransaction(getCustomer(i), t1);
			if (sub2 != null)
			{
				//We keep all the transaction and not only the first in the time
				for (int j=0; j<sub2.size(); j++)
				{
					t2 = sub2.get(j).getDate();
					transactions.add(new Transaction(getCustomer(i), t2));
				}
				
			}
		}
		resetTransactionPointer();
		s.resetTransactionPointer();
		if (!transactions.isEmpty())
		{
			//We clone s1 (faster than creating a new sequence with the same list of ItemSet
			return (new SequenceApparition(transactions));
		}
		return null;
	}

//****************************************************************************************
//Other methods
//****************************************************************************************

	public String toString()
	{
		String str = " : ";
		for (int i=0; i<getTransactions().size(); i++)
		{
			str += getTransaction(i) + " ";
		}
		return str;
	}
	
	public static void main(String[] args)
	{
		Vector <Transaction> v1 = new Vector <Transaction> ();
		Transaction t1 = new Transaction (1,1);
		Transaction t2 = new Transaction (1,2);
		Transaction t5 = new Transaction (2,2);
		Transaction t6 = new Transaction (2,3);
		v1.add(t1); v1.add(t2); v1.add(t5); v1.add(t6);
		SequenceApparition sq1 = new SequenceApparition (v1);
		
		Vector <Transaction> v2 = new Vector <Transaction> ();
		Transaction t3 = new Transaction (1,1);
		Transaction t4 = new Transaction (1,3);
		Transaction t7 = new Transaction (2,1);
		Transaction t8 = new Transaction (2,3);
		v2.add(t3); v2.add(t4); v2.add(t7); v2.add(t8);
		SequenceApparition sq2 = new SequenceApparition (v2);
		System.out.println("sq1 : "+sq1);
		System.out.println("sq2 : "+sq2);
		System.out.println("res : "+sq1.unionSequences(sq2));
	}
}
